package qs.controls
{
	import flash.geom.Point;
	import mx.styles.IStyleClient;
	import flash.utils.getQualifiedClassName;
	import mx.core.UIComponent;
	import mx.styles.CSSStyleDeclaration;
	import mx.styles.StyleManager;
	import flash.system.ApplicationDomain;
	import mx.modules.ModuleManager;
	import mx.core.IFlexModuleFactory;
	import mx.managers.SystemManager;
	import flash.display.DisplayObject;
	import flash.utils.getQualifiedSuperclassName;
	import mx.core.Application;
	
	import mx.core.mx_internal;
	import mx.events.DragEvent;
	import flash.events.MouseEvent;
	
	use namespace mx_internal;

	public class Layout implements ILayout, IStyleClient
	{
		private var _container : FlexibleContainer;
		private var _animator : LayoutAnimator;
		private var _styles : Object = new Object();
		
		// Constructor
		public function Layout()
		{
			regenerateStyleCache(false);
		}
		
		// Getters and Setters
		protected function get container() : FlexibleContainer
		{
			return _container;
		}
		
		protected function get animator() : LayoutAnimator
		{
			return _animator;
		}
				
	    protected function get unscaledContainerWidth():Number
	    {
	        return container.width / Math.abs(container.scaleX);
	    }
	
	    protected function get unscaledContainerHeight():Number
	    {
	        return container.height / Math.abs(container.scaleY);
	    }
	
		
		// ILayout interface
		public function attach(container:FlexibleContainer, animator:LayoutAnimator):void
		{
			_container = container;
			_animator = animator;
		}
		
		public function detach(container : FlexibleContainer) : void
		{
			_container = null;
		}
		
		public function getMeasuredSize():Point
		{
			throw "Not Implemented";
		}
		
		public function findItemAt(px:Number, py:Number, seamAligned:Boolean):Number
		{
			throw "Not Implemented";
		}
		
		public function generateLayout():void
		{
			throw "Not Implemented";
		}

		public function dragStart(e:MouseEvent) : Boolean
		{
			return false;
		}

		public function dragEnter(e:DragEvent) : Boolean
		{
			return false;
		}

		public function dragOver(e:DragEvent) : Boolean
		{
			return false;
		}

		public function dragDrop(e:DragEvent) : Boolean
		{
			return false;
		}

		public function dragComplete(e:DragEvent) : Boolean
		{
			return false;
		}

		public function dragOut(e:DragEvent) : Boolean
		{
			return false;
		}
		// IStyleClient interface
		
	    //----------------------------------
	    //  className
	    //----------------------------------
	
		// Copied verbatim from UIComponent
	    public function get className():String
	    {
	        var name:String = getQualifiedClassName(this);
	        
	        // If there is a package name, strip it off.
	        var index:int = name.indexOf("::");
	        if (index != -1)
	            name = name.substr(index + 2);
	                
	        return name;
	    }
	
	    //----------------------------------
	    //  inheritingStyles
	    //----------------------------------
	
		// Not used.
	    public function get inheritingStyles():Object
	    {
	    	throw "Shouldn't get here";
	    }
	    
		// Not used.
	    public function set inheritingStyles(value:Object):void
	    {
	    	throw "Shouldn't get here";
	    }

	    //----------------------------------
	    //  nonInheritingStyles
	    //----------------------------------
	
		// Not used.
	    public function get nonInheritingStyles():Object
	    {
	    	throw "Shouldn't get here";
	    }
	
		// Not used.
	    public function set nonInheritingStyles(value:Object):void
	    {
	    	throw "Shouldn't get here";
	    }
	
		//----------------------------------
		//  styleDeclaration
		//----------------------------------
		
		// Not used.
	    public function get styleDeclaration():CSSStyleDeclaration
	    {
	    	throw "Shouldn't get here";
	    }
	
		// Not used.
	    public function set styleDeclaration(value:CSSStyleDeclaration):void
	    {
	    	throw "Shouldn't get here";
	    }
	
	    //----------------------------------
	    //  IStyleClient methods
	    //----------------------------------
		
		// Copied verbatim from UIComponent	
		private var _classDeclarations: Array;
		private var _styleNameDeclaration:CSSStyleDeclaration;
		private var _instanceDeclaration:CSSStyleDeclaration = new CSSStyleDeclaration();
	
		// Copied from UIComponent and simplified. No need to worry about inheritance.
		public function getStyle(styleProp:String):*
		{
			var result:*;
			var i : int;

			if (_styleNameDeclaration != null)
			{
				result = _styleNameDeclaration.getStyle(styleProp);
				if (result)
					return result;
			}
			
			// We need to walk backwards through this array in order to get the precedence correct.
			for (i=_classDeclarations.length-1; i >=0; i--)
			{
				var classDecl : CSSStyleDeclaration = _classDeclarations[i];

				result = classDecl.getStyle(styleProp);
				if (result)
					return result;
			}

			return _instanceDeclaration.getStyle(styleProp);
		}
		
		// Copied from UIComponent and simplified. No need to worry about inheritance or proto chains.
		public function setStyle(styleProp:String, newValue:*):void
		{
		    if (styleProp == "styleName")
		    {
		        // Let the setter handle this one, see UIComponent.
		        styleName = newValue;
		
				regenerateStyleCache(false);

		        // Short circuit, because styleName isn't really a style.
		        return;
		    }
		    
		    _instanceDeclaration.mx_internal::setStyle(styleProp, newValue);
		}
		
		// Copied verbatim from UIComponent	
		public function clearStyle(styleProp:String):void
		{
			// TODO: Figure out if this will actually clear values correctly.
		    setStyle(styleProp, undefined);
		}
		
		// Copied from UIComponent and simplified, believe it or not.
	    public function getClassStyleDeclarations():Array
	    {
	        var myApplicationDomain:ApplicationDomain;
	
	        var factory:IFlexModuleFactory = ModuleManager.getAssociatedFactory(this);
	        if (factory != null)
	        {
	            myApplicationDomain = ApplicationDomain(factory.info()["currentDomain"]);
	        }
	        else
	        {
	            var myRoot:DisplayObject = SystemManager.getSWFRoot(this);
	            if (!myRoot)
	                return [];
	            myApplicationDomain = myRoot.loaderInfo.applicationDomain;
	        }
	
	        var className:String = getQualifiedClassName(this)
	        className = className.replace("::", ".");
	        var cache:Array;
	        cache = StyleManager.typeSelectorCache[className];
	        if (cache)
	            return cache;
	        
	        // trace("getClassStyleDeclaration", className);
	        var decls:Array = [];
	        var classNames:Array = [];
	        var caches:Array = [];
	        var declcache:Array = [];
	
	        while (className != null)
	        {
	            var s:CSSStyleDeclaration;
	            cache = StyleManager.typeSelectorCache[className];
	            if (cache)
	            {
	                decls = decls.concat(cache);
	                break;
	            }
	
	            s = StyleManager.getStyleDeclaration(className);
	            
	            if (s)
	            {
	                decls.unshift(s);
	                // we found one so the next set define the selectors for this
	                // found class and its ancestors.  Save the old list and start
	                // a new list
	                classNames.push(className);
	                caches.push(classNames);
	                declcache.push(decls);
	                decls = [];
	                classNames = [];
	                // trace("   ", className);
	                // break;
	            }
	            else
	                classNames.push(className);
	
	            try
	            {
	                className = getQualifiedSuperclassName(myApplicationDomain.getDefinition(className));
	                if (className != null)
		                className = className.replace("::", ".");
	            }
	            catch(e:ReferenceError)
	            {
	                className = null;
	            }
	        }
	
	        caches.push(classNames);
	        declcache.push(decls);
	        decls = [];
	        while (caches.length)
	        {
	            classNames = caches.pop();
	            decls = decls.concat(declcache.pop());
	            while (classNames.length)
	            {
	                StyleManager.typeSelectorCache[classNames.pop()] = decls;
	            }
	        }
	
	        return decls;
	    }
		
		// As far as I can tell, no implementation is needed here. -Sho
	    public function notifyStyleChangeInChildren(styleProp:String, recursive:Boolean):void
		{
			
		}
		
		// TODO: verify that this is correct.
	    public function regenerateStyleCache(recursive:Boolean):void
	    {
	    	// First, get the styleName style. (TODO: can there be more than one?)
	    	_styleNameDeclaration = null;
	    	if (_styleName is String)
	    	{
				_styleNameDeclaration = StyleManager.getStyleDeclaration("." + _styleName as String);
	    	}
			
			// Next, get the class styles.
			_classDeclarations = getClassStyleDeclarations();
	    }
	
		// As far as I can tell, no implementation is needed here. -Sho
	    public function registerEffects(effects:Array /* of String */):void
	    {
	    
	    }
	
	    public function styleChanged(styleProp:String):void
	    {
		    _container.invalidateProperties();
	    }

	    //----------------------------------
	    //  styleName
	    //----------------------------------
	
		// Copied verbatim from UIComponent	
	    private var _styleName:Object /* String, CSSStyleDeclaration, or UIComponent */;

		// Copied verbatim from UIComponent	
	    public function get styleName():Object /* String, CSSStyleDeclaration, or UIComponent */
	    {
	        return _styleName;
	    }

		// Copied from UIComponent and simplified. No need to worry about inheritance or complex invalidations.
		public function set styleName(value:Object /* String, CSSStyleDeclaration, or UIComponent */):void
		{
		    if (_styleName === value)
		        return;
		
		    _styleName = value;

			regenerateStyleCache(false);
		    
		    if (_container)
			    _container.invalidateProperties();
		}
	}
}